/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package rejeev.tracer;

import rejeev.tracer.util.StringUtil;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author a-2533
 */
public class TracerImpl {
    /*
    private boolean console = true;
    private boolean file = true;
    private boolean allThreadsMonitored = false;
    private boolean loggingOn = false;
    private Set<String> watchList = new HashSet<String>();
    */
    private Configuration conf = Configuration.getInstance();
    private String logName = "method-trace-" + System.currentTimeMillis() + ".log";
    PrintWriter out;
    private Map<String, String> threadMap = new HashMap<String, String>();
    
    public TracerImpl(){
        try {
            out = new PrintWriter(new BufferedWriter(new FileWriter(logName), 1024));
            Runtime.getRuntime().addShutdownHook(new Thread(){
                @Override
                public void run(){
                    out.println("Stopping method tracing");
                    stopTracing();
                    out.close();
                }
            });
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    public void entry(String cName, String mName){
        if(!conf.isLoggingEnabled()) return;
        String threadName = Thread.currentThread().getName();
        String indent = threadMap.get(threadName);
        if(indent == null){
            indent = threadName;
            System.out.println(">>>>> Thread " + threadName + " is going to be traced");
        }
        indent = indent + "  ";
        threadMap.put(threadName, indent);
        mName = stripOffBraces(mName);
        log(indent + "<" + cName + "." + mName + ">");
    }
    
    public void exit(String cName, String mName){
        if(!conf.isLoggingEnabled()) return;
        String threadName = Thread.currentThread().getName();
        threadName = StringUtil.replaceSpecialCharacters(threadName);
        String indent = threadMap.get(threadName);
        if(indent == null) return;
        if(indent.length() < threadName.length() + 2) return;
        mName = stripOffBraces(mName);
        log(indent + "</" + cName + "." + mName + ">");
        indent = indent.substring(0, indent.length() - 2);
        threadMap.put(threadName, indent);
    }
    /*
    private boolean isLoggingEnabled(){
        if(!loggingOn) return false;
        if(allThreadsMonitored) return true;
        String threadName = Thread.currentThread().getName();
        if(watchList.contains(threadName)) return true;
        return false;
    }
    */
    public void log(String str){
        if(conf.isConsole()) System.out.println(str);
        if(conf.isFile()) out.println(str);
    }

    private String stripOffBraces(String input){
        if(input.startsWith("<")) input = input.substring(1);
        if(input.endsWith(">")) input = input.substring(0,input.length()-1);
        return input;
    }

    public void stopTracing(){
        conf.enableLogging(false);
        Set<Map.Entry<String,String>> entries = threadMap.entrySet();
        for(Map.Entry<String, String> entry : entries){
            String threadName = entry.getKey();
            String indent = entry.getValue();
            while (indent.length() >= threadName.length() + 2){
                log(indent + "</DummyClass.dummyMethod>");
                indent = indent.substring(0, indent.length() - 2);
            }
        }
        out.flush();
    }

    public void flush(){
        out.flush();
    }

    public void setLogName(String name){
        logName = name;
        try {
            out.close();
            out = new PrintWriter(new BufferedWriter(new FileWriter(logName), 1024));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
    public void addThread(String threadName){
        watchList.add(threadName);
    }
    
    public void removeThread(String threadName){
        watchList.remove(threadName);
    }
    
    public void setAllThreads(boolean allThreads){
        this.allThreadsMonitored = allThreads;
    }
    
    public void setLogHandlers(Set<String> handlers){
       if(handlers.contains("file")){
           file = true;
       }
       if(handlers.contains("console")){
           console = true;
       }
    }
    
    public void enableLogging(boolean isEnabled){
        loggingOn = isEnabled;
    }
    */
}
